package com.example.blog.util;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Classname BeanUtils
 * @Description TODO
 * @Version 1.0.0
 * @Date 2023/7/31 14:39
 * @Created by wlh12
 */
public class BeanUtils {

    /**
     * 默认情况下是会覆盖
     * @param source
     * @param target
     * @throws BeansException
     */
    public static void copyProperties(Object source, Object target) throws BeansException {
        copyProperties(source, target, null, true,(String[]) null);
    }

    public static void copyProperties(Object source, Object target, Class<?> editable) throws BeansException {
        copyProperties(source, target, editable, true, (String[]) null);
    }

    public static void copyProperties(Object source, Object target, String... ignoreProperties) throws BeansException {
        copyProperties(source, target, null, true, ignoreProperties);
    }

    /**
     * flag为false代表为null的不进行copy
     * @param source
     * @param target
     * @param flag
     * @throws BeansException
     */
    public static void copyProperties(Object source, Object target,boolean flag) throws BeansException {
        copyProperties(source, target, null, flag,(String[]) null);
    }

    public static void copyProperties(Object source, Object target, Class<?> editable,boolean flag) throws BeansException {
        copyProperties(source, target, editable, flag, (String[]) null);
    }

    public static void copyProperties(Object source, Object target, boolean flag, String... ignoreProperties) throws BeansException {
        copyProperties(source, target, null, flag, ignoreProperties);
    }

    private static void copyProperties(Object source, Object target, @Nullable Class<?> editable, boolean flag, @Nullable String... ignoreProperties) throws BeansException {
        Assert.notNull(source, "Source must not be null");
        Assert.notNull(target, "Target must not be null");
        Class<?> actualEditable = target.getClass();
        if (editable != null) {
            if (!editable.isInstance(target)) {
                throw new IllegalArgumentException("Target class [" + target.getClass().getName() + "] not assignable to Editable class [" + editable.getName() + "]");
            }

            actualEditable = editable;
        }

        PropertyDescriptor[] targetPds = org.springframework.beans.BeanUtils.getPropertyDescriptors(actualEditable);
        List<String> ignoreList = ignoreProperties != null ? Arrays.asList(ignoreProperties) : null;
        PropertyDescriptor[] var7 = targetPds;
        int var8 = targetPds.length;

        for (int var9 = 0; var9 < var8; ++var9) {
            PropertyDescriptor targetPd = var7[var9];
            Method writeMethod = targetPd.getWriteMethod();
            if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
                PropertyDescriptor sourcePd = org.springframework.beans.BeanUtils.getPropertyDescriptor(source.getClass(), targetPd.getName());
                if (sourcePd != null) {
                    Method readMethod = sourcePd.getReadMethod();
                    if (readMethod != null) {
                        ResolvableType sourceResolvableType = ResolvableType.forMethodReturnType(readMethod);
                        ResolvableType targetResolvableType = ResolvableType.forMethodParameter(writeMethod, 0);
                        boolean isAssignable = !sourceResolvableType.hasUnresolvableGenerics() && !targetResolvableType.hasUnresolvableGenerics() ? targetResolvableType.isAssignableFrom(sourceResolvableType) : ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType());
                        if (isAssignable) {
                            try {
                                if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                                    readMethod.setAccessible(true);
                                }

                                Object value = readMethod.invoke(source);
                                if (flag) {
                                    // 为true说明当源对象属性值为null时覆盖目标对象属性
                                    if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                                        writeMethod.setAccessible(true);
                                    }
                                    writeMethod.invoke(target, value);
                                } else {
                                    // 添加判断条件，当源对象属性值为null时不覆盖目标对象属性
                                    if (value != null) {
                                        if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                                            writeMethod.setAccessible(true);
                                        }
                                        writeMethod.invoke(target, value);
                                    }
                                }

                            } catch (Throwable var18) {
                                throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", var18);
                            }
                        }
                    }
                }
            }
        }
    }

    public static <T> T copyProperties(Object source, Class<T> targetClass) {
        try {
            T target = targetClass
                    .getDeclaredConstructor()
                    .newInstance();
            copyProperties(source, target);
            return target;
        } catch (Exception e) {
            throw new RuntimeException("Failed to copy properties", e);
        }
    }

    public static <S,T> List<T> copyList(List<S> source, Class<T> tClass) {
        try {
            List<T> collect = source.stream()
                    .map(i -> copyProperties(i, tClass))
                    .collect(Collectors.toList());
            return collect;
        } catch (Exception e) {
            throw new RuntimeException("Failed to copy properties", e);
        }
    }

    public static <S,T> Page<T> copyPage(Page<S> source, Class<T> tClass) {
        try {
            Page<T> page = new Page<>();
            copyProperties(source,page,"records");
            List<S> records = source.getRecords();
            List<T> list = copyList(records, tClass);
            page.setRecords(list);
            return page;
        } catch (Exception e) {
            throw new RuntimeException("Failed to copy properties", e);
        }
    }

}
